package simu;

import java.util.ArrayList;
import java.util.List;

import bebetes.Bebete;

/**
 * Un simulateur consiste à agiter une liste d'entités actionnables en appelant
 * la méthode {@link Bebete#agit()} à chaque tick d'horloge correspondant à une
 * attente de <code>delaiSimulation</code> ms.
 * <p>
 * Attention le simulateur est éxécuté dans un thread à part pour de meilleurs
 * performances.
 * 
 * @author COLLET Philippe
 * @author gnpt
 */
public class Simulateur implements Runnable {
	// La liste d'entités actionnables
	protected List<? extends Actionnable> actionnables;
	// Le délai d'attente entre chaque rafraîchissement du simulateur en ms
	protected int delaiSimulation;
	// Le thread dans lequel s'exécute le simulateur
	protected Thread threadSimulation = null;

	/**
	 * Constructeur.
	 * 
	 * Crée un simulateur avec un délai de rafraîchissement (tick d'horloge) du
	 * simulateur de 20ms.
	 */
	public Simulateur() {
		this(20);
	}

	/**
	 * Constructeur.
	 * 
	 * @param delaiSimulation
	 *            le délai de rafraîchissement du simulateur en ms.
	 */
	public Simulateur(int delaiSimulation) {
		this(delaiSimulation, new ArrayList<Actionnable>(0));
	}

	/**
	 * 
	 * @param delaiSimulation
	 *            le délai de rafraîchissement du simulateur en ms.
	 * @param l
	 *            la liste d'entités actionnables.
	 */
	public Simulateur(int delaiSimulation, List<? extends Actionnable> l) {
		this.delaiSimulation = delaiSimulation;
		setActionnables(l);
	}

	/**
	 * Accesseur pour récupérer la liste des entités actionnables.
	 * 
	 * @return les entités actionnables.
	 */
	public List<? extends Actionnable> getActionnables() {
		return this.actionnables;
	}

	/**
	 * Mutateur pour modifier les entités actionnables.
	 * 
	 * @param l
	 *            la nouvelle liste d'entités actionnables.
	 */
	public void setActionnables(List<? extends Actionnable> l) {
		this.actionnables = l;
	}

	/**
	 * Accesseur pour récupérer le délai d'attente entre chaque tick d'horloge
	 * du simulateur en ms.
	 * 
	 * @return le délai d'attente entre chaque tick d'horloge du simulateur en
	 *         ms.
	 */
	public int getDelaiSimulation() {
		return this.delaiSimulation;
	}

	/**
	 * Mutateur pour modifier le délai d'attente entre chaque tick d'horloge du
	 * simulateur en ms.
	 * 
	 * @param delaiSimu
	 *            le nouveau délai d'attente entre chaque tick d'horloge du
	 *            simulateur en ms.
	 */
	public void setDelaiSimulation(int delaiSimu) {
		this.delaiSimulation = delaiSimu;
	}

	/**
	 * Démarre la simulation : crée un nouveau thread dans lequel le simulateur
	 * va s'exécuter et le lance.
	 */
	public void demarre() {
		this.threadSimulation = new Thread(this);
		this.threadSimulation.start();
	}

	/**
	 * Arrête la simulation.
	 */
	public void arrete() {
		this.threadSimulation = null; // ceci sera testé dans le run()
	}

	/**
	 * Agite les entités actionnables.
	 * 
	 * @see Bebete#agit()
	 */
	public void agiterLesActionnables() {
		for (Actionnable a : this.actionnables) {
			a.agit();
		}
	}

	/**
	 * A chaque tick d'horloge, le thread agite les entités actionnables.
	 * 
	 * @see #agiterLesActionnables()
	 */
	@Override
	public void run() {
		// Code du thread pour arrêt propre (cf. sun technical tips)
		Thread currentThread = Thread.currentThread();
		while (this.threadSimulation == currentThread) {
			synchronized (this.actionnables) {
				this.agiterLesActionnables();
			}
			try {
				Thread.sleep(this.delaiSimulation);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
